var latlng = {};
/**
 * 获取鼠标当前的屏幕坐标位置的三维Cesium坐标
 * @param {Cesium.Scene} scene 
 * @param {Cesium.Cartesian2} position 二维屏幕坐标位置
 * @param {Cesium.Entity} noPickEntity 排除的对象（主要用于绘制中，排除对自己本身的拾取）
 */
latlng.getCurrentMousePosition = function (scene, position, noPickEntity) {
    var cartesian;

    //在模型上提取坐标  
    var pickedObject = scene.pick(position);
    if (scene.pickPositionSupported && Cesium.defined(pickedObject)) { //pickPositionSupported :判断是否支持深度拾取,不支持时无法进行鼠标交互绘制
        var entity = pickedObject.id;
        if (noPickEntity == null || (noPickEntity && entity !== noPickEntity)) {
            var cartesian = scene.pickPosition(position);
            if (Cesium.defined(cartesian)) {
                var cartographic = Cesium.Cartographic.fromCartesian(cartesian);
                var height = cartographic.height; //模型高度
                if (height >= 0) return cartesian;

                //不是entity时，支持3dtiles地下
                if (!Cesium.defined(pickedObject.id) && height >= -500)
                    return cartesian;
            }
        }
    }

    //测试scene.pickPosition和globe.pick的适用场景 https://zhuanlan.zhihu.com/p/44767866
    //1. globe.pick的结果相对稳定准确，不论地形深度检测开启与否，不论加载的是默认地形还是别的地形数据；
    //2. scene.pickPosition只有在开启地形深度检测，且不使用默认地形时是准确的。
    //注意点： 1. globe.pick只能求交地形； 2. scene.pickPosition不仅可以求交地形，还可以求交除地形以外其他所有写深度的物体。

    //提取鼠标点的地理坐标 
    if (scene.mode === Cesium.SceneMode.SCENE3D) {
        //三维模式下
        var pickRay = scene.camera.getPickRay(position);
        cartesian = scene.globe.pick(pickRay, scene);
    } else {
        //二维模式下
        cartesian = scene.camera.pickEllipsoid(position, scene.globe.ellipsoid);
    }
    return cartesian;
}

/**
 * 提取地球中心点坐标
 * @param {Cesium.Viewer} viewer 
 * */
latlng.getCenter = function (viewer) {
    var scene = viewer.scene;
    var target = pickCenterPoint(scene);
    var bestTarget = target;
    if (!bestTarget) {
        var globe = scene.globe;
        var carto = scene.camera.positionCartographic.clone();
        var height = globe.getHeight(carto);
        carto.height = height || 0;
        bestTarget = Cesium.Ellipsoid.WGS84.cartographicToCartesian(carto);
    }

    var result = latlng.formatPositon(bestTarget);

    // 获取地球中心点世界位置  与  摄像机的世界位置  之间的距离
    var distance = Cesium.Cartesian3.distance(bestTarget, viewer.scene.camera.positionWC);
    result.cameraZ = distance;

    return result;
}

function pickCenterPoint(scene) {
    var canvas = scene.canvas;
    var center = new Cesium.Cartesian2(
        canvas.clientWidth / 2,
        canvas.clientHeight / 2);

    var ray = scene.camera.getPickRay(center);
    var target = scene.globe.pick(ray, scene);
    return target || scene.camera.pickEllipsoid(center);
}

/**
 * 提取地球视域边界
 * @param {Cesium.Viewer} viewer 
 * */
latlng.getExtent = function (viewer) {
    // 范围对象
    var extent = {
        xmin: 70,
        xmax: 140,
        ymin: 0,
        ymax: 55
    }; //默认值：中国区域

    // 得到当前三维场景
    var scene = viewer.scene;

    // 得到当前三维场景的椭球体
    var ellipsoid = scene.globe.ellipsoid;
    var canvas = scene.canvas;

    // canvas左上角
    var car3_lt = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(0, 0), ellipsoid);
    if (car3_lt) { // 在椭球体上
        var carto_lt = ellipsoid.cartesianToCartographic(car3_lt);
        extent.xmin = Cesium.Math.toDegrees(carto_lt.longitude);
        extent.ymax = Cesium.Math.toDegrees(carto_lt.latitude);
    } else { // 不在椭球体上 
        var xMax = canvas.width / 2;
        var yMax = canvas.height / 2;

        var car3_lt2;
        // 这里每次10像素递加，一是10像素相差不大，二是为了提高程序运行效率
        for (var yIdx = 0; yIdx <= yMax; yIdx += 10) {
            var xIdx = yIdx <= xMax ? yIdx : xMax;
            car3_lt2 = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(xIdx, yIdx), ellipsoid);
            if (car3_lt2) break;
        }
        if (car3_lt2) {
            var carto_lt = ellipsoid.cartesianToCartographic(car3_lt2);
            extent.xmin = Cesium.Math.toDegrees(carto_lt.longitude);
            extent.ymax = Cesium.Math.toDegrees(carto_lt.latitude);
        }
    }

    // canvas右下角
    var car3_rb = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(canvas.width, canvas.height), ellipsoid);
    if (car3_rb) { // 在椭球体上
        var carto_rb = ellipsoid.cartesianToCartographic(car3_rb);
        extent.xmax = Cesium.Math.toDegrees(carto_rb.longitude);
        extent.ymin = Cesium.Math.toDegrees(carto_rb.latitude);
    } else { // 不在椭球体上
        var xMax = canvas.width / 2;
        var yMax = canvas.height / 2;

        var car3_rb2;
        // 这里每次10像素递减，一是10像素相差不大，二是为了提高程序运行效率
        for (var yIdx = canvas.height; yIdx >= yMax; yIdx -= 10) {
            var xIdx = yIdx >= xMax ? yIdx : xMax;
            car3_rb2 = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(xIdx, yIdx), ellipsoid);
            if (car3_rb2) break;
        }
        if (car3_rb2) {
            var carto_rb = ellipsoid.cartesianToCartographic(car3_rb2);
            extent.xmax = Cesium.Math.toDegrees(carto_rb.longitude);
            extent.ymin = Cesium.Math.toDegrees(carto_rb.latitude);
        }
    }

    //交换
    if (extent.xmax < extent.xmin) {
        var temp = extent.xmax;
        extent.xmax = extent.xmin;
        extent.xmin = temp;
    }
    if (extent.ymax < extent.ymin) {
        var temp = extent.ymax;
        extent.ymax = extent.ymin;
        extent.ymin = temp;
    }


    return extent;
}
/**
 * 提取视域边界
 * @param {Cesium.Viewer} viewer 
 * 
 * */
latlng.getViewBounds = function (viewer) {
    var rectangle = viewer.camera.computeViewRectangle();
    // 弧度转为经纬度，west为左（西）侧边界的经度，以下类推
    var west = rectangle.west / Math.PI * 180;
    var north = rectangle.north / Math.PI * 180;
    var east = rectangle.east / Math.PI * 180;
    var south = rectangle.south / Math.PI * 180;
    // 鉴于高德、leaflet获取的边界都是southwest和northeast字段来表示，本例保持一致性
    var bounds = {
        southwest: {
            lng: west,
            lat: south
        },
        northeast: {
            lng: east,
            lat: north
        }
    }
    return bounds;
}

/**
 * 提取相机视角范围参数
 * @param {Cesium.Viewer} viewer 
 * 
 * */
latlng.getCameraView = function (viewer) {
    var camera = viewer.camera;
    var position = camera.positionCartographic;

    var bookmark = {};
    bookmark.y = Number(Cesium.Math.toDegrees(position.latitude).toFixed(6));
    bookmark.x = Number(Cesium.Math.toDegrees(position.longitude).toFixed(6));
    bookmark.z = Number(position.height.toFixed(1));
    bookmark.heading = Number(Cesium.Math.toDegrees(camera.heading || -90).toFixed(1));
    bookmark.pitch = Number(Cesium.Math.toDegrees(camera.pitch || 0).toFixed(1));
    bookmark.roll = Number(Cesium.Math.toDegrees(camera.roll || 0).toFixed(1));

    return bookmark;
}


/**
 * 格式化坐标点为可显示的可理解格式（如：经度x:123.345345、纬度y:31.324324、高度z:123.1）。
 * @param {Cesium.Cartesian3} position 
 * */
latlng.formatPositon = function (position) {
    var carto = Cesium.Cartographic.fromCartesian(position);
    var result = {};
    result.y = Number(Cesium.Math.toDegrees(carto.latitude).toFixed(6));
    result.x = Number(Cesium.Math.toDegrees(carto.longitude).toFixed(6));
    result.z = Number(carto.height.toFixed(1));
    return result;
};

//笛卡尔转经纬度/弧度
latlng.Cartesian3To = function (cartesian3, viewer) {
    if (cartesian3) {
        if (viewer) {
            var ellipsoid = viewer.scene.globe.ellipsoid;
            var cartographic;
            //转弧度
            cartographic = ellipsoid.cartesianToCartographic(cartesian3);
        } else {
            cartographic = Cesium.Cartographic.fromCartesian(cartesian3);
        }
        //转经纬度
        var lat = Cesium.Math.toDegrees(cartographic.latitude);
        var lng = Cesium.Math.toDegrees(cartographic.longitude);
        var height = cartographic.height;
        return {
            cartesian3: cartesian3,
            cartographic: cartographic,
            latlng: {
                lat: lat,
                lng: lng,
                height: height
            }
        };
    }
}
//绕点 环绕飞行
latlng.windingPoint = {
    isStart: false,
    viewer: null,
    start: function (viewer, point) {
        if (!point)
            point = getCenter(viewer);

        this.viewer = viewer;
        this.position = Cesium.Cartesian3.fromDegrees(point.x, point.y, point.z);

        this.distance = point.distance || Cesium.Cartesian3.distance(this.position, viewer.camera.positionWC); // 给定相机距离点多少距离飞行 
        this.angle = 360 / (point.time || 60); //time：给定飞行一周所需时间(单位 秒)

        this.time = viewer.clock.currentTime.clone();
        this.heading = viewer.camera.heading; // 相机的当前heading
        this.pitch = viewer.camera.pitch;

        this.viewer.clock.onTick.addEventListener(this.clock_onTickHandler, this);
        this.isStart = true;
    },
    clock_onTickHandler: function (e) {
        var delTime = Cesium.JulianDate.secondsDifference(this.viewer.clock.currentTime, this.time); // 当前已经过去的时间，单位 秒
        var heading = Cesium.Math.toRadians(delTime * this.angle) + this.heading;

        this.viewer.scene.camera.setView({
            destination: this.position, // 点的坐标
            orientation: {
                heading: heading,
                pitch: this.pitch,
            }
        });
        this.viewer.scene.camera.moveBackward(this.distance);
    },
    stop: function () {
        if (!this.isStart) return;

        if (this.viewer)
            this.viewer.clock.onTick.removeEventListener(this.clock_onTickHandler, this);
        this.isStart = false;
    }
};

//固定点 向四周旋转
latlng.aroundPoint = {
    isStart: false,
    viewer: null,
    start: function (viewer, point) {
        if (!point)
            point = getCenter(viewer);

        this.viewer = viewer;
        this.position = Cesium.Cartesian3.fromDegrees(point.x, point.y, point.z);

        this.angle = 360 / (point.time || 60); //time：给定飞行一周所需时间(单位 秒)

        this.time = viewer.clock.currentTime.clone();
        this.heading = viewer.camera.heading; // 相机的当前heading
        this.pitch = viewer.camera.pitch;

        this.viewer.clock.onTick.addEventListener(this.clock_onTickHandler, this);
        this.isStart = true;
    },
    clock_onTickHandler: function (e) {
        // 当前已经过去的时间，单位s
        var delTime = Cesium.JulianDate.secondsDifference(this.viewer.clock.currentTime, this.time);
        var heading = Cesium.Math.toRadians(delTime * this.angle) + this.heading;
        viewer.scene.camera.setView({
            orientation: {
                heading: heading,
                pitch: this.pitch,
            }
        });
    },
    stop: function () {
        if (!this.isStart) return;

        if (this.viewer)
            this.viewer.clock.onTick.removeEventListener(this.clock_onTickHandler, this);
        this.isStart = false;
    }
};